/**
 * @see java.lang.ref.ReferenceQueue
 * 引用队列ReferenceQueue 可以与软引用、弱引用以及虚引用一起配合使用
 * ReferenceQueue内部数据结构是一个链表
 * ReferenceQueue是使用wati()和notifyAll()实现生产者和消费者模式的一个具体场景。
 *
 * link https://blog.csdn.net/firebolt100/article/details/82660333
 * 使用缓存会提高性能吗？
 * 答案是这需要根据具体情况分析，如果从test获取需要缓存的数据量较少，使用缓存会非常合适且一定会提升性能。
 * 但假若需要从test表查询放到缓存里的数据量非常大，那就会出现一个问题：由于数据量过大可能会导致内存不足，而不单单是提升性能了。
 * 假如说把表中所有数据都放入缓存，那么缓存的可能会占据大部分jvm的内存或者索性直接产生一个OOM错误。
 *
 * 解决方案
 * 最佳的方案是如果我们可以创造一种可以按需扩展和收缩的动态缓存，
 * 当我们的数据量需要而内存充裕的时候可以适当增加，
 * 但内存不足是可以按不同方案对其进行回收。
 *
 * reference设计目的
 * 这里引出的一个问题，就是为什么要在Java中使用不同类型的reference？
 * (1)我们的应用在运行过程中会产生很多对象，这些对象驻留在内存中，它们大小不同，重要性不同，使用频率不同，生命周期不同，
 *    比如有些对象只要应用启动就一直存活直到应用停止，而有些对象生命周期与创建它的线程相同（例ThreadLocalMap），还有些对象只作临时变量短时间就消亡，
 *    再比如某些缓存数据，内存充裕的时候可以存活，内存不足的时候可能需要被首先牺牲被回收，
 * 所以很容易想象对于不同的对象，我们希望对他们的创建销毁采取不同的策略，
 * 可是不幸的是java不像C一样可以由开发者决定对象的析构销毁，而是将管理内存的活统一交给了jvm进行gc，但jvm显然不知道这些对象的区别。
 * 于是设计者们在java 1.2加入了reference，使jvm可以对不同的reference对象采取不同的回收策略以达到提高应用性能的目的。
 *
 * @see java.lang.ref.Reference
 * @see java.lang.ref.WeakReference
 * 实际上java.lang.ref包中就有以下几种不同的reference类型，分别是：
 * StrongReference(实际并没有发现 StrongReference 类型，基本上new对象都是强引用)
 * SoftReference
 * WeakReference
 * PhantomReference
 * @see java.lang.ref.PhantomReference
 *
 *
 * FinalReference
 * @see java.lang.ref.FinalReference
 * @see java.lang.ref.Finalizer FinalReference的子类
 * @see java.lang.ref.Finalizer.FinalizerThread
 *   1）与Finalizer相关联的则是Object中的finalize()方法，在类加载的过程中，
 *   2）如果当前类有覆写finalize()方法，则其对象会被标记为finalizer类，这种类型的对象被回收前会先调用其finalize()。
 *   具体的实现机制是，在gc进行可达性分析的时候，如果当前对象是finalizer类型的对象，并且本身不可达（与GC Roots无相连接的引用），
 *   则会被加入到一个ReferenceQueue类型的队列(F-Queue)中。
 *   而系统在初始化的过程中，会启动一个FinalizerThread实例的守护线程(线程名Finalizer)，
 *   该线程会不断消费F-Queue中的对象，并执行其finalize()方法(runFinalizer)，
 *   并且runFinalizer方法会捕获Throwable级别的异常，也就是说finalize()方法的异常不会导致FinalizerThread运行中断退出。
 *   对象在执行finalize()方法后，只是断开了与Finalizer的关联，并不意味着会立即被回收，还是要等待下一次GC，而每个对象的finalize()方法都只会执行一次，不会重复执行。
 *
 *   3）finalize()方法是对象逃脱死亡命运的最后一次机会，
 *    如果在该方法中将对象本身(this关键字) 赋值给某个类变量或者对象的成员变量，那在第二次标记时它将被移出"即将回收的集合"。
 *  4）注意：finalize()使用不当会导致内存泄漏和内存溢出，比如SocksSocketImpl之类的服务会在finalize()中加入close()操作用于释放资源，
 *  但是如果FinalizerThread一直没有执行的话就会导致资源一直无法释放，从而出现内存泄漏。
 *  还有如果某对象的finalize()方法执行时间太长或者陷入死循环，将导致F-Queue一直堆积，从而造成内存溢出(oom)。
 *
 * 对象可达性判断
 * 当前主流java虚拟机都是采用 GC Roots Tracing 算法，比如 Sun 的 Hotspot 虚拟机便是采用该算法。
 * java虚拟机进行gc时，判断一个对象的被引用情况决定是否回收，都是从根节点引用（Root set of Reference）开始标识可达路径的。
 * 对于某个对象可能会存在其多个引用，且这多个引用的类型不同。
 *
 * 内存泄露
 * 内存泄露为程序在申请内存后，无法释放已申请的内存空间，一次内存泄露危害可以忽略，但内存泄露堆积后果很严重，无论多少内存,迟早会被占光，
 * 广义并通俗的说，就是：不再会被使用的对象或者变量占*用的内存不能被回收，就是内存泄露。

 * 强引用与弱引用
 * 强引用，使用最普遍的引用，一个对象具有强引用，不会被垃圾回收器回收。当内存空间不足，Java虚拟机宁愿抛出OutOfMemoryError错误，使程序异常终止，也不回收这种对象。
 * 如果想取消强引用和某个对象之间的关联，可以显式地将引用赋值为null，这样可以使JVM在合适的时间就会回收该对象。
 * 弱引用，JVM进行垃圾回收时，无论内存是否充足，都会回收被弱引用关联的对象。在java中，用java.lang.ref.WeakReference类来表示。可以在缓存中使用弱引用。
 *
 * GC回收机制-如何找到需要回收的对象
 * JVM如何找到需要回收的对象，方式有两种：
 * (1)引用计数法：每个对象有一个引用计数属性，新增一个引用时计数加1，引用释放时计数减1，计数为0时可以回收，
 * (2)可达性分析法：从 GC Roots 开始向下搜索，搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时，则证明此对象是不可用的，那么虚拟机就判断是可回收对象。
 * 问题：引用计数法，可能会出现A 引用了 B，B 又引用了 A，这时候就算他们都不再使用了，但因为相互引用 计数器=1 永远无法被回收。
 *
 * 那些对象可以当做GC Roots
 * （1）虚拟机栈（栈帧中的局部变量区，也叫做局部变量表）中的引用对象
 * （2）方法区中的类静态属性引用的对象
 * （3）方法区中常量引用的对象
 * （4）本地方法栈中的JNI（Native方法）的引用对象
 * @date 2020/12/16
 */
package cn.jdemo.ref;